#include "in.h"

struct MemDataPage *CreateDataPage( void *subject_to_object , size_t dataunit_size , struct list_head *datapage_list , size_t *p_datapage_count )
{
	struct MemDataPage	*datapage = NULL ;
	size_t			dataunit_prealloc_count ;
	size_t			page_size ;
	
	dataunit_prealloc_count = sizeof0(struct MemDataPage,dataunit_used_bitmap) * 8 ;
	page_size = sizeof(struct MemDataPage) + dataunit_size * dataunit_prealloc_count ;
	datapage = (struct MemDataPage *)malloc( page_size ) ;
	if( datapage == NULL )
		return NULL;
	memset( datapage , 0x00 , page_size );
	
	datapage->subject_to_object = subject_to_object ;
	datapage->p_datapage_count = p_datapage_count ;
	datapage->dataunit_size = dataunit_size ;
	datapage->dataunit_prealloc_count = dataunit_prealloc_count ;
	datapage->dataunit_used_count = 0 ;
	datapage->dataunit_used_bitmap = 0 ;
	
	list_add_tail( & (datapage->datapage_list_node) , datapage_list );
	
	(*p_datapage_count)++;
	
	return datapage;
}

void DestroyDataPage( struct MemDataPage *datapage )
{
#if MDB_TRACE
	uint64_t			used_bitmap ;
	char				*dataunit = NULL ;
	
	used_bitmap = 1 ;
	used_bitmap = ( used_bitmap << (sizeof0(struct MemDataPage,dataunit_used_bitmap)*8-1) ) ;
	dataunit = datapage->dataunit_array_base ;
	while( used_bitmap )
	{
		if( used_bitmap & datapage->dataunit_used_bitmap )
		{
			TRACE_PRINTF( "REMAIN dataunit[%p] used_bitmap[0x%lX]\n" , dataunit , datapage->dataunit_used_bitmap );
		}
		
		used_bitmap >>= 1 ;
		dataunit += datapage->dataunit_size ;
	}
#endif
	
	(*(datapage->p_datapage_count))--;
	
	list_del( & (datapage->datapage_list_node) );
	free( datapage );
	
	return;
}

void FreeDataPageDirectly( struct list_head *datapage_list )
{
	struct MemDataPage	*datapage = NULL ;
	struct MemDataPage	*next_datapage = NULL ;
	
	list_for_each_entry_safe( datapage , next_datapage , datapage_list , struct MemDataPage , datapage_list_node )
	{
		TRACE_PRINTF( "free datapage[%p] , subject_to_object[%p][%s] *p_datapage_count[%zu] , dataunit_size[%zu] dataunit_used_count[%zu/%zu] dataunit_used_bitmap[0x%02lX]\n" , datapage , datapage->subject_to_object , (char*)(datapage->subject_to_object) , *(datapage->p_datapage_count) , datapage->dataunit_size , datapage->dataunit_used_count , datapage->dataunit_prealloc_count , datapage->dataunit_used_bitmap )
		DestroyDataPage( datapage );
	}
	
	return;
}

size_t GetDataUnitSize( struct MemDataPage *datapage )
{
	return datapage->dataunit_size;
}

size_t GetPreAllocCount( struct MemDataPage *datapage )
{
	return datapage->dataunit_prealloc_count;
}

size_t GetUsedCount( struct MemDataPage *datapage )
{
	return datapage->dataunit_prealloc_count;
}

unsigned char IsDataPageEmpty( struct MemDataPage *datapage )
{
	if( datapage->dataunit_used_count == 0 )
		return 1;
	else
		return 0;
}

unsigned char IsDataPageFull( struct MemDataPage *datapage )
{
	if( datapage->dataunit_used_count == datapage->dataunit_prealloc_count )
		return 1;
	else
		return 0;
}

char *GetUnusedDataUnit( struct MemDataPage *datapage )
{
	uint64_t	dataunit_no ;
	char		*dataunit = NULL ;
	uint64_t	setbit_used_bitmap ;
	
	if( IsDataPageFull(datapage) )
	{
		tls_last_errno = MDB_ERROR_RECORD_DATAPAGE_IS_FULL ;
		return NULL;
	}
	
	dataunit_no = __builtin_clzl( ~(datapage->dataunit_used_bitmap) ) ;
	dataunit = datapage->dataunit_array_base + datapage->dataunit_size * dataunit_no ;
	memset( dataunit , 0x00 , datapage->dataunit_size );
	
	setbit_used_bitmap = 1 ;
	setbit_used_bitmap = ( setbit_used_bitmap << (sizeof(datapage->dataunit_used_bitmap)*8-1-dataunit_no) ) ;
	datapage->dataunit_used_bitmap |= setbit_used_bitmap ;
	datapage->dataunit_used_count++;
	
	return dataunit;
}

void SetDataUnitUnused( struct MemDataPage *datapage , char *dataunit )
{
	uint64_t		dataunit_no ;
	uint64_t		unsetbit_used_bitmap ;
	
	memset( dataunit , 0x00 , datapage->dataunit_size );
	
	dataunit_no = ( dataunit - datapage->dataunit_array_base ) / datapage->dataunit_size ;
	
	unsetbit_used_bitmap = 1 ;
	unsetbit_used_bitmap = ( unsetbit_used_bitmap << (sizeof(datapage->dataunit_used_bitmap)*8-1-dataunit_no) ) ;
	datapage->dataunit_used_bitmap &= ~unsetbit_used_bitmap ;
	datapage->dataunit_used_count--;
	
	if( datapage->dataunit_used_count == 0 )
	{
		if( *(datapage->p_datapage_count) >= MDB_DATAPAGE_CACHE_RESERVED_COUNT+1 )
			DestroyDataPage( datapage );
	}
	
	return;
}

